package com.yixuan.mt.common.service.impl_mysql;

import com.yixuan.mt.common.cache.ObjectsCache;
import com.yixuan.mt.common.constant.PathConstant;
import com.yixuan.mt.common.entity.Objects;
import com.yixuan.mt.common.exception.HttpBadRequestException;
import com.yixuan.mt.common.mapper.ObjectsMapper;
import com.yixuan.mt.common.pool.EventsConnectionManage;
import com.yixuan.mt.common.request.UploadRequest;
import com.yixuan.mt.common.service.ObjectsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class ObjectsServiceMysqlImpl implements ObjectsService {

    @Autowired
    private ObjectsMapper objectsMapper;

    @Autowired
    private ObjectsCache objectsCache;

    @Autowired
    private EventsConnectionManage eventsConnectionManage;

    /**
     * 新增对象（不创建文件）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void postObjects(String objectPath, File file) {
        String parentPath = objectPath.substring(0, objectPath.lastIndexOf("/"));
        String objectName = objectPath.substring(objectPath.lastIndexOf("/") + 1);

        // 唯一性检查（并不能保证唯一）
        if (objectsMapper.selectIsObjectExist(objectName, parentPath)) {
            throw new HttpBadRequestException("对象名已存在！");
        }

        /* 判断父级路径是否存在, 不存在则创建文件夹对象 */
        String objectParentPath = parentPath;
        List<Objects> stack = new ArrayList<>();
        while (!objectsCache.selectIsParentPathExist(parentPath)) {
            // 直接就在这里删除缓存, 删除操作不影响一致性。
            objectsCache.deleteParentPathCache(parentPath);

            int index = parentPath.lastIndexOf("/");
            String parent = parentPath.substring(0, index);
            String folderName = parentPath.substring(index + 1);
            // 构建文件夹对象
            Objects folder = new Objects();
            folder.setName(folderName);
            folder.setSize(0L);
            folder.setType("folder");
            folder.setParentPath(parent);
            folder.setLastModifiedTime(System.currentTimeMillis());
            stack.add(folder);

            parentPath = parent;
        }
        if (!stack.isEmpty()) {
            objectsMapper.insertObjects(stack);
        }

        // 构建文件对象
        Objects objects = new Objects();
        objects.setType("file");
        objects.setName(objectName);
        objects.setSize(file.length());
        objects.setLastModifiedTime(file.lastModified());
        objects.setParentPath(objectParentPath);

        String bucketName = objectPath.substring(1, objectPath.indexOf("/", 1));
        try {
            // 这里每次都会去数据库更新对象数量，后面优化成更新缓存。
            // 项目启动时先去数据库遍历统计对象数量，然后进行缓存，而不是从对象数量字段中获取，避免数据不一致。
            objectsMapper.updateAddObjectNum(bucketName);
            objectsMapper.insertObject(objects);
            // 在最后才发起事件通知，确保对象已经创建成功才去发送事件通知。
            // 且当若发起事件失败，也会借助事务回滚，保证操作的原子性。
            eventsConnectionManage.sendEvents(objects);
        } catch (Exception e) {
            file.delete();
            throw e;
        }
    }

    /**
     * 新增对象（上传文件）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void upload(UploadRequest uploadRequest) throws IOException {

        String objectPath = uploadRequest.getObjectPath();
        MultipartFile object = uploadRequest.getObjectFile();
        String parentPath = objectPath.substring(0, objectPath.lastIndexOf("/"));
        String objectName = objectPath.substring(objectPath.lastIndexOf("/") + 1);

        String fullPath = PathConstant.FILE_RESOURCE_PATH_PREFIX_MYSQL + objectPath + "/" + objectName;
        File file = new File(fullPath);
        File parentFile = file.getParentFile();

        // 唯一性检查（并不能保证唯一）
        if (objectsMapper.selectIsObjectExist(objectName, parentPath)) {
            throw new HttpBadRequestException("对象名已存在！");
        }

        // 更新缓存
        objectsCache.deleteParentPathCache(parentPath);

        // 创建文件
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        object.transferTo(file);

        Objects objects = new Objects();
        objects.setName(objectName);
        objects.setType(object.getContentType());
        objects.setSize(file.length());
        objects.setLastModifiedTime(file.lastModified());
        objects.setParentPath(parentPath);

        String bucketName = objectPath.substring(1, objectPath.indexOf("/", 1));
        try {
            objectsMapper.updateAddObjectNum(bucketName);
            objectsMapper.insertObject(objects);
            // 发起事件通知
            eventsConnectionManage.sendEvents(objects);
        } catch (Exception e) {
            file.delete();
            throw e;
        }
    }

    @Override
    public void postFolder(String folderPath) {
        String objectName = folderPath.substring(folderPath.lastIndexOf("/") + 1);
        // 获取父级路径
        String parentPath = folderPath.substring(0, folderPath.lastIndexOf("/"));

        // 唯一性检查
        if (objectsMapper.selectIsObjectExist(objectName, parentPath)) {
            throw new HttpBadRequestException("文件名冲突！");
        }

        // 更新缓存
        objectsCache.deleteParentPathCache(folderPath);

        // 构建
        Objects objects = new Objects();
        objects.setName(objectName);
        objects.setType("folder");
        objects.setSize(0L);
        objects.setLastModifiedTime(System.currentTimeMillis());
        objects.setParentPath(parentPath);

        String bucketName = folderPath.substring(1, folderPath.indexOf("/", 1));
        objectsMapper.updateAddObjectNum(bucketName);
        objectsMapper.insertObject(objects);
    }

    @Override
    public void deleteObjects(String name, List<Integer> ids) {
        objectsMapper.deleteObjects(ids);
        objectsMapper.updateReduceObjectNum(name, ids.size());
    }

    @Override
    public List<Objects> getObjects(Integer lastId, String parentPath) throws IOException {
        return objectsMapper.selectObjects(lastId, parentPath);
    }

    @Override
    public ResponseEntity<InputStreamResource> download(String objectPath) throws FileNotFoundException {
        FileInputStream fi = new FileInputStream(PathConstant.FILE_RESOURCE_PATH_PREFIX_MYSQL + objectPath + objectPath.substring(objectPath.lastIndexOf("/")));
        InputStreamResource isr = new InputStreamResource(fi);

        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .header("Content-Disposition", "attachment;filename=" + objectPath.substring(objectPath.lastIndexOf("/") + 1))
                .body(isr);
    }

    /**
     * 使用这个需要确保桶是存在的，因为如果parentPath是 “/桶名” 则会直接返回true。
     */
    @Override
    public Boolean isParentPathExist(String parentPath) {
        // 获取parentPath的parent
        int index = parentPath.lastIndexOf("/");
        if (index == 0) {
            return true;
        }
        String parent = parentPath.substring(0, index);
        String objectName = parentPath.substring(index + 1);
        return objectsMapper.selectIsObjectExist(objectName, parent);
    }
}
